package org.ly.uap.client.validation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.ly.uap.client.authentication.AttributePrincipal;
import org.ly.uap.client.authentication.AttributePrincipalImpl;
import org.ly.uap.client.proxy.Cas20ProxyRetriever;
import org.ly.uap.client.proxy.ProxyGrantingTicketStorage;
import org.ly.uap.client.proxy.ProxyRetriever;
import org.ly.uap.client.util.CommonUtils;
import org.ly.uap.client.util.XmlUtils;

public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTicketValidator {
    private String proxyCallbackUrl;
    private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
    private ProxyRetriever proxyRetriever;

    public Cas20ServiceTicketValidator(String casServerUrlPrefix) {
        super(casServerUrlPrefix);
        this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding());
    }

    protected final void populateUrlAttributeMap(Map<String, String> urlParameters) {
        urlParameters.put("pgtUrl", encodeUrl(this.proxyCallbackUrl));
    }

    protected String getUrlSuffix() {
        return "serviceValidate";
    }

    protected final Assertion parseResponseFromServer(String response) throws TicketValidationException {
        String error = XmlUtils.getTextForElement(response,
                "authenticationFailure");

        if (CommonUtils.isNotBlank(error)) {
            throw new TicketValidationException(error);
        }

        String principal = XmlUtils.getTextForElement(response, "user");
        String proxyGrantingTicketIou = XmlUtils.getTextForElement(
                response, "proxyGrantingTicket");
        String proxyGrantingTicket = this.proxyGrantingTicketStorage != null ? this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou) : null;

        if (CommonUtils.isEmpty(principal)) {
            throw new TicketValidationException("No principal was found in the response from the CAS server.");
        }

        Map attributes = extractCustomAttributes(response);
        Assertion assertion;
        if (CommonUtils.isNotBlank(proxyGrantingTicket)) {
            AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, proxyGrantingTicket, this.proxyRetriever);
            assertion = new AssertionImpl(attributePrincipal);
        } else {
            assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes));
        }

        customParseResponse(response, assertion);

        return assertion;
    }

    protected Map<String, Object> extractCustomAttributes(String xml) {
        int pos1 = xml.indexOf("<cas:attributes>");
        int pos2 = xml.indexOf("</cas:attributes>");

        if (pos1 == -1) {
            return Collections.emptyMap();
        }

        String attributesText = xml.substring(pos1 + 16, pos2);

        Map attributes = new HashMap();
        BufferedReader br = new BufferedReader(new StringReader(attributesText));

        List<String> attributeNames = new ArrayList<>();
        int leftPos;
        try {
            String line;
            while ((line = br.readLine()) != null) {
                String trimmedLine = line.trim();
                if (trimmedLine.length() > 0) {
                    leftPos = trimmedLine.indexOf(":");
                    int rightPos = trimmedLine.indexOf(">");
                    attributeNames.add(trimmedLine.substring(leftPos + 1, rightPos));
                }
            }
            br.close();
        } catch (IOException localIOException) {
        }
        for (String name : attributeNames) {
            List values = XmlUtils.getTextForElements(xml, name);

            if (values.size() == 1)
                attributes.put(name, values.get(0));
            else {
                attributes.put(name, values);
            }
        }

        return attributes;
    }

    protected void customParseResponse(String response, Assertion assertion)
            throws TicketValidationException {
    }

    public final void setProxyCallbackUrl(String proxyCallbackUrl) {
        this.proxyCallbackUrl = proxyCallbackUrl;
    }

    public final void setProxyGrantingTicketStorage(ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
        this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
    }

    public final void setProxyRetriever(ProxyRetriever proxyRetriever) {
        this.proxyRetriever = proxyRetriever;
    }
}
